From ef4690d511e051a75ec948fab00035e3f5007939 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 2 Sep 2011 20:04:06 -0400 Subject: [PATCH] GtkGrid: make attaching more flexible Allow to attach children at either end of row/column 0. Proposed by Alex Larsson. https://bugzilla.gnome.org/show_bug.cgi?id=657793 --- gtk/gtkgrid.c | 189 +++++++++++++++++++++------------ gtk/tests/Makefile.am | 4 + gtk/tests/grid.c | 238 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 366 insertions(+), 65 deletions(-) create mode 100644 gtk/tests/grid.c diff --git a/gtk/gtkgrid.c b/gtk/gtkgrid.c index cfb21f41ba..df6d74daec 100644 --- a/gtk/gtkgrid.c +++ b/gtk/gtkgrid.c @@ -363,41 +363,93 @@ gtk_grid_init (GtkGrid *grid) priv->linedata[1].homogeneous = FALSE; } -static void grid_attach (GtkGrid *grid, - GtkWidget *child, - gint left, - gint top, - gint width, - gint height); - static void -gtk_grid_add (GtkContainer *container, - GtkWidget *child) +grid_attach (GtkGrid *grid, + GtkWidget *widget, + gint left, + gint top, + gint width, + gint height) +{ + GtkGridPrivate *priv = grid->priv; + GtkGridChild *child; + + child = g_slice_new (GtkGridChild); + child->widget = widget; + CHILD_LEFT (child) = left; + CHILD_TOP (child) = top; + CHILD_WIDTH (child) = width; + CHILD_HEIGHT (child) = height; + + priv->children = g_list_prepend (priv->children, child); + + gtk_widget_set_parent (widget, GTK_WIDGET (grid)); +} + +/* Find the position 'touching' existing + * children. @orientation and @max determine + * from which direction to approach (horizontal + * + max = right, vertical + !max = top, etc). + * @op_pos, @op_span determine the rows/columns + * in which the touching has to happen. + */ +static gint +find_attach_position (GtkGrid *grid, + GtkOrientation orientation, + gint op_pos, + gint op_span, + gboolean max) { - GtkGrid *grid = GTK_GRID (container); GtkGridPrivate *priv = grid->priv; GtkGridChild *grid_child; GtkGridChildAttach *attach; GtkGridChildAttach *opposite; GList *list; gint pos; + gboolean hit; + + if (max) + pos = -G_MAXINT; + else + pos = G_MAXINT; + + hit = FALSE; - pos = 0; for (list = priv->children; list; list = list->next) { grid_child = list->data; - attach = &grid_child->attach[priv->orientation]; - opposite = &grid_child->attach[1 - priv->orientation]; + attach = &grid_child->attach[orientation]; + opposite = &grid_child->attach[1 - orientation]; + + /* check if the ranges overlap */ + if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span) + { + hit = TRUE; - if (opposite->pos <= 0 && opposite->pos + opposite->span > 0) - pos = MAX (pos, attach->pos + attach->span); + if (max) + pos = MAX (pos, attach->pos + attach->span); + else + pos = MIN (pos, attach->pos); + } } - if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) - grid_attach (grid, child, pos, 0, 1, 1); - else - grid_attach (grid, child, 0, pos, 1, 1); + if (!hit) + pos = 0; + + return pos; +} + +static void +gtk_grid_add (GtkContainer *container, + GtkWidget *child) +{ + GtkGrid *grid = GTK_GRID (container); + GtkGridPrivate *priv = grid->priv; + gint pos[2] = { 0, 0 }; + + pos[priv->orientation] = find_attach_position (grid, priv->orientation, 0, 1, TRUE); + grid_attach (grid, child, pos[0], pos[1], 1, 1); } static void @@ -1365,29 +1417,6 @@ gtk_grid_new (void) return g_object_new (GTK_TYPE_GRID, NULL); } -static void -grid_attach (GtkGrid *grid, - GtkWidget *widget, - gint left, - gint top, - gint width, - gint height) -{ - GtkGridPrivate *priv = grid->priv; - GtkGridChild *child; - - child = g_slice_new (GtkGridChild); - child->widget = widget; - CHILD_LEFT (child) = left; - CHILD_TOP (child) = top; - CHILD_WIDTH (child) = width; - CHILD_HEIGHT (child) = height; - - priv->children = g_list_prepend (priv->children, child); - - gtk_widget_set_parent (widget, GTK_WIDGET (grid)); -} - /** * gtk_grid_attach: * @grid: a #GtkGrid @@ -1424,7 +1453,8 @@ gtk_grid_attach (GtkGrid *grid, * gtk_grid_attach_next_to: * @grid: a #GtkGrid * @child: the widget to add - * @sibling: the child of @grid that @child will be placed next to + * @sibling (allow-none): the child of @grid that @child will be placed + * next to, or %NULL to place @child at the beginning or end * @side: the side of @sibling that @child is positioned next to * @width: the number of columns that @child will span * @height: the number of rows that @child will span @@ -1432,7 +1462,9 @@ gtk_grid_attach (GtkGrid *grid, * Adds a widget to the grid. * * The widget is placed next to @sibling, on the side determined by - * @side. + * @side. When @sibling is %NULL, the widget is placed in row (for + * left or right placement) or column 0 (for top or bottom placement), + * at the end indicated by @side. */ void gtk_grid_attach_next_to (GtkGrid *grid, @@ -1448,32 +1480,59 @@ gtk_grid_attach_next_to (GtkGrid *grid, g_return_if_fail (GTK_IS_GRID (grid)); g_return_if_fail (GTK_IS_WIDGET (child)); g_return_if_fail (gtk_widget_get_parent (child) == NULL); - g_return_if_fail (gtk_widget_get_parent (sibling) == (GtkWidget*)grid); + g_return_if_fail (sibling == NULL || gtk_widget_get_parent (sibling) == (GtkWidget*)grid); g_return_if_fail (width > 0); g_return_if_fail (height > 0); - grid_sibling = find_grid_child (grid, sibling); + if (sibling) + { + grid_sibling = find_grid_child (grid, sibling); - switch (side) + switch (side) + { + case GTK_POS_LEFT: + left = CHILD_LEFT (grid_sibling) - width; + top = CHILD_TOP (grid_sibling); + break; + case GTK_POS_RIGHT: + left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling); + top = CHILD_TOP (grid_sibling); + break; + case GTK_POS_TOP: + left = CHILD_LEFT (grid_sibling); + top = CHILD_TOP (grid_sibling) - height; + break; + case GTK_POS_BOTTOM: + left = CHILD_LEFT (grid_sibling); + top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling); + break; + default: + g_assert_not_reached (); + } + } + else { - case GTK_POS_LEFT: - left = CHILD_LEFT (grid_sibling) - width; - top = CHILD_TOP (grid_sibling); - break; - case GTK_POS_RIGHT: - left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling); - top = CHILD_TOP (grid_sibling); - break; - case GTK_POS_TOP: - left = CHILD_LEFT (grid_sibling); - top = CHILD_TOP (grid_sibling) - height; - break; - case GTK_POS_BOTTOM: - left = CHILD_LEFT (grid_sibling); - top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling); - break; - default: - g_assert_not_reached (); + switch (side) + { + case GTK_POS_LEFT: + left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE); + top = 0; + break; + case GTK_POS_RIGHT: + left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE); + top = 0; + break; + case GTK_POS_TOP: + left = 0; + top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE); + break; + case GTK_POS_BOTTOM: + left = 0; + top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE); + break; + default: + g_assert_not_reached (); + } } grid_attach (grid, child, left, top, width, height); diff --git a/gtk/tests/Makefile.am b/gtk/tests/Makefile.am index 4b2d3440ef..b4d224cc0a 100644 --- a/gtk/tests/Makefile.am +++ b/gtk/tests/Makefile.am @@ -123,6 +123,10 @@ TEST_PROGS += entry entry_SOURCES = entry.c entry_LDADD = $(progs_ldadd) +TEST_PROGS += grid +grid_SOURCES = grid.c +grid_LDADD = $(progs_ldadd) + EXTRA_DIST += \ file-chooser-test-dir/empty \ file-chooser-test-dir/text.txt diff --git a/gtk/tests/grid.c b/gtk/tests/grid.c new file mode 100644 index 0000000000..90be1fda26 --- /dev/null +++ b/gtk/tests/grid.c @@ -0,0 +1,238 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2011 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +/* test that attach_next_to picks the places + * we expect it to pick, when there is any choice + */ +static void +test_attach (void) +{ + GtkGrid *g; + GtkWidget *child, *sibling, *z, *A, *B; + gint left, top, width, height; + + g = (GtkGrid *)gtk_grid_new (); + + child = gtk_label_new ("a"); + gtk_grid_attach_next_to (g, child, NULL, GTK_POS_LEFT, 1, 1); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 0); + g_assert_cmpint (top, ==, 0); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 1); + + sibling = child; + child = gtk_label_new ("b"); + gtk_grid_attach_next_to (g, child, sibling, GTK_POS_RIGHT, 2, 2); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 1); + g_assert_cmpint (top, ==, 0); + g_assert_cmpint (width, ==, 2); + g_assert_cmpint (height, ==, 2); + + /* this one should just be ignored */ + z = gtk_label_new ("z"); + gtk_grid_attach (g, z, 4, 4, 1, 1); + + child = gtk_label_new ("c"); + gtk_grid_attach_next_to (g, child, sibling, GTK_POS_BOTTOM, 3, 1); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 0); + g_assert_cmpint (top, ==, 1); + g_assert_cmpint (width, ==, 3); + g_assert_cmpint (height, ==, 1); + + child = gtk_label_new ("u"); + gtk_grid_attach_next_to (g, child, z, GTK_POS_LEFT, 2, 1); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 2); + g_assert_cmpint (top, ==, 4); + g_assert_cmpint (width, ==, 2); + g_assert_cmpint (height, ==, 1); + + child = gtk_label_new ("v"); + gtk_grid_attach_next_to (g, child, z, GTK_POS_RIGHT, 2, 1); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 5); + g_assert_cmpint (top, ==, 4); + g_assert_cmpint (width, ==, 2); + g_assert_cmpint (height, ==, 1); + + child = gtk_label_new ("x"); + gtk_grid_attach_next_to (g, child, z, GTK_POS_TOP, 1, 2); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 4); + g_assert_cmpint (top, ==, 2); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 2); + + child = gtk_label_new ("x"); + gtk_grid_attach_next_to (g, child, z, GTK_POS_TOP, 1, 2); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 4); + g_assert_cmpint (top, ==, 2); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 2); + + child = gtk_label_new ("y"); + gtk_grid_attach_next_to (g, child, z, GTK_POS_BOTTOM, 1, 2); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 4); + g_assert_cmpint (top, ==, 5); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 2); + + A = gtk_label_new ("A"); + gtk_grid_attach (g, A, 10, 10, 1, 1); + B = gtk_label_new ("B"); + gtk_grid_attach (g, B, 10, 12, 1, 1); + + child = gtk_label_new ("D"); + gtk_grid_attach_next_to (g, child, A, GTK_POS_RIGHT, 1, 3); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 11); + g_assert_cmpint (top, ==, 10); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 3); +} + +static void +test_add (void) +{ + GtkGrid *g; + GtkWidget *child; + gint left, top, width, height; + + g = (GtkGrid *)gtk_grid_new (); + + gtk_orientable_set_orientation (GTK_ORIENTABLE (g), GTK_ORIENTATION_HORIZONTAL); + + child = gtk_label_new ("a"); + gtk_container_add (GTK_CONTAINER (g), child); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 0); + g_assert_cmpint (top, ==, 0); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 1); + + child = gtk_label_new ("b"); + gtk_container_add (GTK_CONTAINER (g), child); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 1); + g_assert_cmpint (top, ==, 0); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 1); + + child = gtk_label_new ("c"); + gtk_container_add (GTK_CONTAINER (g), child); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 2); + g_assert_cmpint (top, ==, 0); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 1); + + gtk_orientable_set_orientation (GTK_ORIENTABLE (g), GTK_ORIENTATION_VERTICAL); + + child = gtk_label_new ("d"); + gtk_container_add (GTK_CONTAINER (g), child); + gtk_container_child_get (GTK_CONTAINER (g), child, + "left-attach", &left, + "top-attach", &top, + "width", &width, + "height", &height, + NULL); + g_assert_cmpint (left, ==, 0); + g_assert_cmpint (top, ==, 1); + g_assert_cmpint (width, ==, 1); + g_assert_cmpint (height, ==, 1); +} + +int +main (int argc, + char *argv[]) +{ + gtk_test_init (&argc, &argv); + + g_test_add_func ("/grid/attach", test_attach); + g_test_add_func ("/grid/add", test_add); + + return g_test_run(); +} -- 2.30.2